﻿using System;
using System.Collections;

namespace Utils
{
	static internal class Mathematics
	{
		/// <summary>
		/// Bitwise XOR for 2 byte arrays.  Arrays must be the same length.
		/// </summary>
		/// <param name="t1">Left side for comparison</param>
		/// <param name="t2">Right side for comparison</param>
		/// <returns>Resulting byte array</returns>
		static internal byte[] BitwiseXOR(byte[] t1, byte[] t2)
		{
			//Inputs need to be the same length	for this implementation
			if (!(t1.Length == t2.Length)) {
				throw new ArgumentException("Input arrays must have the same length");
			}

			byte[] result = new byte[t1.Length];
			byte bytXOR = 0;

			for (int i = 0; i <= t1.Length - 1; i++) {
				bytXOR = BitwiseXOR(t1[i], t2[i]);

				result[i] = bytXOR;
			}

			return result;
		}

		/// <summary>
		/// Bitwise XOR for 2 Bytes.
		/// </summary>
		/// <param name="t1">Left side for comparison</param>
		/// <param name="t2">Right side for comparison</param>
		/// <returns>Resulting byte</returns>
		static internal byte BitwiseXOR(byte t1, byte t2)
		{
			BitArray baLft = null;
			BitArray baRght = null;
			BitArray baXor = null;

			//Have to use Byte Arrays as the constructor for the BitArray, otherwise the 
			//integer value of the current byte is used to set the length of the bitArray instead of the value.
			byte[] bytL = new byte[1];
			byte[] bytR = new byte[1];

			bytL[0] = t1;
			bytR[0] = t2;
			baLft = new BitArray(bytL);
			baRght = new BitArray(bytR);
			baXor = baLft.Xor(baRght);

			byte[] ba2BytArr = new byte[8];

			baXor.CopyTo(ba2BytArr, 0);

			return ba2BytArr[0];
		}

		/// <summary>
		/// Convert the input Integer to an Octet String.
		/// </summary>
		/// <param name="x">input integer</param>
		/// <param name="size">size in octets (bytes)</param>
		/// <returns>Resulting byte array of specified length</returns>
		static internal byte[] I2OSP(int x, int size)
		{
			byte[] bytVal = BitConverter.GetBytes(x);

			byte[] result = new byte[size];
			Buffer.BlockCopy(bytVal, 0, result, (result.Length - bytVal.Length), bytVal.Length);

			Array.Reverse(result);

			return result;
		}

		/// <summary>
		/// Mask generation function.
		/// </summary>
		/// <param name="seed">Seed</param>
		/// <param name="maskLen">Length of generated mask</param>
		/// <param name="hashLength">Length of the hash produced by the supplied hash provider</param>
		/// <param name="hashProvider">Hash provider to use in mask generation</param>
		/// <returns>Generated mask of specified length</returns>
		static internal byte[] OAEPMGF(byte[] seed, int maskLen, int hashLength, HashProviders.IHashProvider hashProvider)
		{
			byte[] result = new byte[maskLen];

			//Determine how many interations we have to do.  We'll be appending 
			//m_hLen (hash length) bytes for every iteration, so the size of the generated byte array 
			//will be m_hLen * iNum (number of iterations).
            double temp = maskLen / hashLength;
            int iNum = (int)Math.Floor(temp) + 1;
						
			//Mask that will be truncated to create the final 
			//resulting mask returned by this function.
			byte[] bytLongMask = new byte[(iNum * hashLength)];

			byte[] bytAppend = new byte[4];
			byte[] bytTmp = null;
			int iPadLen = 0;
			byte[] bytSeedHash = new byte[hashLength];

			//Padded pseudorandom seed to be hashed.
			byte[] bytPadSeed = new byte[(seed.Length + 4)];
			seed.CopyTo(bytPadSeed, 0);

			for (int i = 0; i <= iNum - 1; i++) {
				//Convert the iterator to an Octet String byte array
				bytTmp = Mathematics.I2OSP(i, 4);

				//Calculate the needed padding zeros, and add 
				//them to the resulting Array.  Result must be 4 bytes long.
				iPadLen = bytAppend.Length - bytTmp.Length;

				bytTmp.CopyTo(bytAppend, 0);

				//Hash the pseudorandom padded seed and append it to the 
				//long version of the mask.
				bytAppend.CopyTo(bytPadSeed, seed.Length);
				bytSeedHash = hashProvider.ComputeHash(bytPadSeed);
				bytSeedHash.CopyTo(bytLongMask, i * hashLength);
			}

			//Copy the first maskLen bytes of bytLongMask to the result
			//and return the result.
			Array.Copy(bytLongMask, result, maskLen);

			return result;

		}
	}

	static internal class DigestEncoding
	{
		static internal byte[] SHA1()
		{
			return new byte[] {
				0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 
				0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14
			};

		}

		static internal byte[] SHA256()
		{
			return new byte[] {
				0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 
				0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
			};

		}

		static internal byte[] SHA384()
		{
			return new byte[] {
				0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 
				0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30
			};

		}

		static internal byte[] SHA512()
		{
			return new byte[] {
				0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 
				0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40
			};

		}

		static internal byte[] MD2()
		{
			return new byte[] {
				0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 
				0x86, 0xf7, 0x0d, 0x02, 0x02, 0x05, 0x00, 0x04, 0x10
			};

		}

		static internal byte[] MD5()
		{
			return new byte[] {
				0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 
				0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10
			};

		}
	}

}